﻿using SimpleLiteForm.Helpers;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;

namespace SimpleLiteForm
{
    /// <summary>
    /// Manages all the OCR stuff using Tessnet2 OCR.
    /// </summary>
    public class OcrManager : IDisposable
    {
        /// <summary>
        /// The OCR engine.
        /// </summary>
        tessnet2.Tesseract _ocr;

        /// <summary>
        /// The screen width.
        /// </summary>
        private int _screenWidth;

        /// <summary>
        /// The screen height.
        /// </summary>
        private int _screenHeight;

        /// <summary>
        /// The default padding of the selected OCR region.
        /// </summary>
        private const int DEFAULT_PADDING = 25;

        /// <summary>
        /// The position1 of the screen selection.
        /// </summary>
        private Point _startPosition;

        /// <summary>
        /// The position2 of the screen selection.
        /// </summary>
        private Point _endPosition;

        /// <summary>
        /// The raw result of the last OCR run.
        /// </summary>
        List<tessnet2.Word> _rawResult = new List<tessnet2.Word>();

        /// <summary>
        /// The filtered result of the last OCR run.
        /// </summary>
        List<tessnet2.Word> _filteredResult = new List<tessnet2.Word>();

        /// <summary>
        /// Creates a new OCR manager.
        /// </summary>
        /// <param name="screenWidth">The screen width.</param>
        /// <param name="screenHeigth">The screen height.</param>
        public OcrManager(int screenWidth, int screenHeigth)
        {
            _ocr = new tessnet2.Tesseract();
            _ocr.SetVariable("tessedit_char_whitelist", "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!?.,-\"\'=%&()+/");
            _ocr.Init(@"../../../../lib/tessdata", "eng", false);

            _screenWidth = screenWidth;
            _screenHeight = screenHeigth;
        }

        /// <summary>
        /// Updates the OCR data.
        /// </summary>
        /// <param name="image"></param>
        public void Update(Bitmap image)
        {
            ClearResults();

            _rawResult = _ocr.DoOCR(image, RectangleForOcr); // TODO: fix StackOverflowException here...
            //filtered words
            foreach (var rawWord in _rawResult)
            {
                if (rawWord.Text.Length > 1 && rawWord.Confidence < 175)
                {
                    _filteredResult.Add(rawWord);
                }
            }
        }

        /// <summary>
        /// Clears all previous results.
        /// </summary>
        public void ClearResults()
        {
            _rawResult.Clear();
            _filteredResult.Clear();
        }

        /// <summary>
        /// Gets the raw result.
        /// </summary>
        public List<tessnet2.Word> RawResult
        {
            get
            {
                return _rawResult;
            }
        }

        /// <summary>
        /// Gets the filtered result.
        /// </summary>
        public List<tessnet2.Word> FilteredResult
        {
            get
            {
                return _filteredResult;
            }
        }

        /// <summary>
        /// Gets the selected OCR rectangle of the screen.
        /// </summary>
        public Rectangle RectangleForOcr
        {
            get
            {
                int x = Math.Min(_startPosition.X, _endPosition.X);
                int y = Math.Min(_startPosition.Y, _endPosition.Y);
                int width = Math.Abs(_startPosition.X - _endPosition.X);
                int height = Math.Abs(_startPosition.Y - _endPosition.Y);

                return new Rectangle(x, y, width, height);
            }
        }

        /// <summary>
        /// Verifies whether the selected OCR rectangle is valid.
        /// </summary>
        public bool IsRectangleForOcrValid
        {
            get
            {
                if (RectangleForOcr.Height == 0 || RectangleForOcr.Width == 0)
                    return false;

                return true;
            }
        }

        /// <summary>
        /// The position1 for the screen selection.
        /// </summary>
        public Point StartPosition
        {
            get
            {
                return _startPosition;
            }
            set
            {
                int x = MathHelper.Clamp(value.X, 0, _screenWidth);
                int y = MathHelper.Clamp(value.Y, 0, _screenHeight);

                _startPosition = new Point(x, y);
            }
        }

        /// <summary>
        /// The position2 for the screen selection.
        /// </summary>
        public Point EndPosition
        {
            get
            {
                return _endPosition;
            }
            set
            {
                int x = MathHelper.Clamp(value.X, 0, _screenWidth);
                int y = MathHelper.Clamp(value.Y, 0, _screenHeight);

                _endPosition = new Point(x, y);
            }
        }

        /// <summary>
        /// Cleans up the unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            if (_ocr != null)
                _ocr.Dispose();
        }
    }
}
